#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>
#include "Channel.h"
#include "EventLoop.h"
#include <sys/select.h>

#define MAX 1024

struct SelectData
{
    fd_set readfds;
    fd_set writefds;
    int32_t maxFd;
};

static void *selectInit();
static int32_t selectAdd(struct Channel *channel, struct EventLoop *evLoop);
static int32_t selectRemove(struct Channel *channel, struct EventLoop *evLoop);
static int32_t selectModify(struct Channel *channel, struct EventLoop *evLoop);
static int32_t selectDispatch(struct EventLoop *evLoop, int32_t timeout);
static int32_t selectClear(struct EventLoop *evLoop);
static int32_t setFdSet(struct Channel *channel, struct EventLoop *evLoop);
static int32_t clearFdSet(struct Channel *channel, struct EventLoop *evLoop);

struct Dispatcher SelectDispatcher =
    {
        selectInit,
        selectAdd,
        selectRemove,
        selectModify,
        selectDispatch,
        selectClear};

static void *selectInit()
{
    struct SelectData *SelectData = (struct SelectData *)malloc(sizeof(struct SelectData));
    if (SelectData == NULL)
    {
        perror("malloc error");
        exit(0);
    }
    FD_ZERO(&SelectData->readfds);
    FD_ZERO(&SelectData->writefds);
    SelectData->maxFd = 0;

    return SelectData;
}

static int32_t selectAdd(struct Channel *channel, struct EventLoop *evLoop)
{
    if (setFdSet(channel, evLoop) == -1)
    {
        return -1;
    }
    return 0;
}

static int32_t selectRemove(struct Channel *channel, struct EventLoop *evLoop)
{
    if (clearFdSet(channel, evLoop) == -1)
    {
        return -1;
    }
    return 0;
}

static int32_t selectModify(struct Channel *channel, struct EventLoop *evLoop)
{
    if (clearFdSet(channel, evLoop) == -1)
    {
        return -1;
    }
    if (setFdSet(channel, evLoop) == -1)
    {
        return -1;
    }
    //通过channel释放对于的tcpConnection资源
    channel->destroyCallback(channel->arg);
    return 0;
}

static int32_t selectDispatch(struct EventLoop *evLoop, int32_t timeout)
{
    struct SelectData *SelectData = (struct SelectData *)evLoop->dispatcherData;
    struct timeval val;
    val.tv_sec = timeout;
    val.tv_usec = 0;
    fd_set rdtmp = SelectData->readfds;
    fd_set wrtmp = SelectData->writefds;
    int32_t count = select(SelectData->maxFd + 1, &rdtmp, &wrtmp, NULL, &val);
    if (count == -1)
    {
        perror("select error");
    }
    for (int32_t i = 0; i < SelectData->maxFd + 1; i++)
    {
        if (FD_ISSET(i, &rdtmp))
        {
            eventActivate(evLoop, i, ReadEvent);
        }
        if (FD_ISSET(i, &wrtmp))
        {
            eventActivate(evLoop, i, WriteEvent);
        }
    }
    return 0;
}

static int32_t selectClear(struct EventLoop *evLoop)
{
    struct SelectData *SelectData = (struct SelectData *)evLoop->dispatcherData;
    free(SelectData);
}

static int32_t setFdSet(struct Channel *channel, struct EventLoop *evLoop)
{
    struct SelectData *SelectData = (struct SelectData *)evLoop->dispatcherData;
    if (channel->fd >= MAX)
    {
        return -1;
    }
    if (channel->events & ReadEvent)
    {
        FD_SET(channel->fd, &SelectData->readfds);
    }
    if (channel->events & WriteEvent)
    {
        FD_SET(channel->fd, &SelectData->writefds);
    }
    SelectData->maxFd = (SelectData->maxFd > channel->fd) ? SelectData->maxFd : channel->fd;
    return 0;
}

static int32_t clearFdSet(struct Channel *channel, struct EventLoop *evLoop)
{
    struct SelectData *SelectData = (struct SelectData *)evLoop->dispatcherData;
    if (channel->fd >= MAX)
    {
        return -1;
    }
    if (channel->events & ReadEvent)
    {
        FD_CLR(channel->fd, &SelectData->readfds);
    }
    if (channel->events & WriteEvent)
    {
        FD_CLR(channel->fd, &SelectData->writefds);
    }

    if (channel->fd == SelectData->maxFd)
    {
        int32_t newMax = -1;
        for (int32_t i = 0; i < SelectData->maxFd; i++)
        {
            if (FD_ISSET(i, &SelectData->readfds) || FD_ISSET(i, &SelectData->writefds))
            {
                newMax = i;
            }
        }
        SelectData->maxFd = (newMax == -1) ? 0 : newMax;
    }
    return 0;
}
